package com.heima.article.service.impl;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApArticleContent;
import com.heima.article.entity.ApAuthor;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleContentService;
import com.heima.article.service.ApArticleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.service.ApAuthorService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.threadpool.ThreadPoolConfig;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.HotArticleVo;
import com.heima.model.media.dtos.WmNewsResultDTO;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author HM
 * @since 2023-02-01
 */
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    public static final String KEY_PREFIX = "leadnews:article:score";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void findTimeForArticle(int i) {
        LocalDateTime dateTime = LocalDate.now().minusDays(5).atStartOfDay();
        LambdaQueryWrapper<ApArticle> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ApArticle::getIsDown,false).eq(ApArticle::getIsDelete,false);
        lqw.gt(ApArticle::getPublishTime,dateTime);
        List<ApArticle> apArticleList = this.list(lqw);
        if (CollectionUtils.isEmpty(apArticleList)) {
            log.info("没有需要计算分值的文章");
            return;
        }
        //        计算文章分值
        for (ApArticle article : apArticleList) {
            Long score = calcArticleScore(article);
            redisTemplate.boundZSetOps(KEY_PREFIX).add(""+article.getId(),score);

        }

    }

    /**
     * 计算文章分支
     * @param article
     * @return
     */
    private Long calcArticleScore(ApArticle article) {
        long score = 0;

        if (article.getViews() != null){
            score += article.getViews();
        }
        if (article.getLikes() != null){

            score += 3*article.getLikes();
        }

        if (article.getComments() != null){

            score += 5*article.getComments();
        }

        if (article.getCollection() != null){

            score += 8*article.getCollection();
        }

        return score;
    }

    @Override
    public ArticleDto findById(Long articleId) {

        return this.getBaseMapper().findById(articleId);

    }

    @Autowired
    private ApArticleContentService apArticleContentService;



    private ExecutorService taskExecutor = new ThreadPoolExecutor
            (Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors()*2,
            120L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1000),
            new ThreadPoolExecutor.CallerRunsPolicy());

    @Autowired
    private ArticleFreemarkerService freemarkerService;


    @Autowired
    private ApArticleMapper apArticleMapper;


    @Override
    public List<ArticleDto> findByPage(int page, int size) {
        IPage iPage = new Page(page,size);
        IPage<ArticleDto> apArticleIPage = apArticleMapper.findByPage(iPage);
        if (apArticleIPage == null || CollectionUtils.isEmpty(apArticleIPage.getRecords())){
            return null;
        }
        return apArticleIPage.getRecords();

    }



    /**
     * 自媒体文章审核通过以后，feign调用，创建article
     * */
    @Override
    public Long createArticle(WmNewsResultDTO dto) {
        Integer wmNewsId = dto.getId();

        LambdaQueryWrapper<ApArticle> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ApArticle::getWmNewsId,wmNewsId);
        ApArticle apArticle = this.getOne(lqw);

        ApArticle article = buildApArticleSave(apArticle,dto);
        this.saveOrUpdate(article);

        // article content
      ApArticleContent apArticleContent = buildArticleContent(article,dto);
      apArticleContentService.saveOrUpdate(apArticleContent);

        // 异步生成详情的静态html，并上传minio
        freemarkerService.buildContentHtml(article.getId(),dto.getContent());


      return article.getId();


    }
    // 单页最大加载的数字
    private final static short MAX_PAGE_SIZE = 10;

    @Override
    public List<ApArticle> load(ArticleHomeDto dto, int type) {

        if (dto.getChannelId() != null && dto.getChannelId() == 0){
            return loadRedis(dto);
        }
        Integer size = dto.getSize();
        if (size == null || size == 0) {
            size = 10;
        }
        size = Math.min(size, MAX_PAGE_SIZE);

        IPage<ApArticle> iPage = new Page<>(1, size);
        QueryWrapper<ApArticle> queryWrapper = new QueryWrapper<>();
//        判断是否传递了频道信息
        if(dto.getChannelId()!= null && dto.getChannelId() !=0 ){
//          选择了频道
            queryWrapper.lambda().eq(ApArticle::getChannelId,dto.getChannelId());
        }
//        查询时间
        if(type == 1){
            queryWrapper.lambda().lt(ApArticle::getPublishTime,dto.getMinTime());
        }else{
            queryWrapper.lambda().gt(ApArticle::getPublishTime,dto.getMaxTime());
        }
        queryWrapper.lambda().
                eq(ApArticle::getIsDown,false).
                eq(ApArticle::getIsDelete,false)
                .orderByDesc(ApArticle::getPublishTime);
//      分页查询
        IPage<ApArticle> apArticleIPage = page(iPage,queryWrapper);
        if(apArticleIPage != null && !CollectionUtils.isEmpty(apArticleIPage.getRecords())){
            List<ApArticle> articleDtoList = BeanHelper.copyWithCollection(apArticleIPage.getRecords(), ApArticle.class);
            return articleDtoList;
        }
        return Collections.emptyList();

    }

    /**
     * 展示推荐频道
     * @param dto
     */
    private List<ApArticle> loadRedis(ArticleHomeDto dto) {
        Set<String> articleIds = redisTemplate.boundZSetOps(KEY_PREFIX).reverseRangeByScore(0, Double.MAX_VALUE);
        List<Long> ids = articleIds.stream().map(Long::parseLong).collect(Collectors.toList());
        List<ApArticle> apArticleList = this.listByIds(ids);
        if (CollectionUtils.isEmpty(apArticleList)){
            return null;
        }
        Map<Long, ApArticle> articleMap = apArticleList.stream().collect(Collectors.toMap(ApArticle::getId, value -> value, (k1, k2) -> k1));
        List<ApArticle> list = new ArrayList<>();
        for (String articleId : articleIds) {
            ApArticle article = articleMap.get(Long.parseLong(articleId));

            list.add(article);
        }

        return list;
//        List<ArticleDto> list = new ArrayList<>();
//        String redisKey = KEY_PREFIX + 0;
//
//        Set<String> ids = redisTemplate.opsForZSet().reverseRange(redisKey, 0, -1);
//        BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(KEY_PREFIX);
//        for (String id : ids) {
//            String jsonStr = boundHashOps.get(id);
//            HotArticleVo hotArticleVo = JsonUtils.toBean(jsonStr, HotArticleVo.class);
//            ArticleDto articleDto = BeanHelper.copyProperties(hotArticleVo, ArticleDto.class);
//            list.add(articleDto);
//        }
//        return list;
    }

    @Override
    public void upOrDown(Map<String, Long> map) {
        //        map  key  - articleId    key - enable value - 1 :上架  0：下架
        UpdateWrapper<ApArticle> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().
                eq(ApArticle::getId,map.get("articleId")).
                set(ApArticle::getIsDown,map.get("enable").intValue()==1?false:true);
        update(updateWrapper);
    }

    private ApArticleContent buildArticleContent(ApArticle article, WmNewsResultDTO dto) {
        ApArticleContent articleContent = new ApArticleContent();
        articleContent.setArticleId(article.getId());
        articleContent.setContent(dto.getContent());
        return articleContent;
    }

    @Autowired
    private ApAuthorService apAuthorService;

    private ApArticle buildApArticleSave(ApArticle apArticle, WmNewsResultDTO dto) {
        ApArticle article = new ApArticle();
        article.setId(apArticle == null ? null:apArticle.getId());
        article.setTitle(dto.getTitle());
        //查询文章作者 名字 和id
        ApAuthor author = apAuthorService.findAuthor(dto.getWmUserId());
        article.setAuthorId(author.getId());
        article.setAuthorName(author.getName());
        article.setChannelId(dto.getChannelId());
        article.setChannelName(dto.getChannelName());
        article.setLayout(dto.getType());
        article.setFlag(0);
        article.setImages(dto.getImages());
        article.setLabels(dto.getLabels());
        article.setLikes(0L);
        article.setCollection(0L);
        article.setComments(0L);
        article.setViews(0L);
        if (apArticle == null){
            article.setCreatedTime(new Date());
        }
        article.setPublishTime(new Date());
        article.setIsComment(true);
        article.setIsForward(true);
        article.setIsDown(false);
        article.setIsDelete(false);
        article.setWmNewsId(dto.getId());
        return article;



    }

}
